#include <stdlib.h>
#include <stdbool.h>

#include <assert.h>

#include "threads_queue.h"

struct tq {
	struct node * head;
	struct node * tail;
};

struct node {
	struct node * next;
	thread_id payload;
};

struct tq * tq_init()
{
	struct tq * tq = NULL;
	if (!(tq = malloc(sizeof(*tq))))
		goto err;

	tq->head = NULL;
	tq->tail = NULL;
err:
	return tq;
}

void tq_fini(struct tq * tq)
{
	if (!tq)
		return;
	while(tq->head)
		tq_remove(tq, NULL);
	free(tq);
	tq = NULL;
}

int tq_add(struct tq * tq, thread_id p)
{
	int ret = -1;
	struct node * node = NULL;
	if (!(node = malloc(sizeof(*node))))
		goto err;
	node->next = NULL;
	node->payload = p;
	if (tq->tail) {
		tq->tail->next = node;
		tq->tail = node;
	} else {
		tq->head = node;
		tq->tail = node;
	}

	ret = 0;
err:
	return ret;

}

int tq_peek(struct tq * tq, thread_id * p)
{
	int ret = -1;
	if ((!tq) || (!tq->head) || (!p))
		goto out;
	*p = tq->head->payload;
	ret = 0;
out:
	return ret;
}
int tq_remove(struct tq * tq, thread_id * p)
{
	int ret = -1;
	struct node * node;
	if ((!tq) || (!tq->head))
		goto err;

	node = tq->head;
	tq->head = node->next;
	if (!tq->head)
		tq->tail = NULL;
	if (NULL != p) {
		*p = node->payload;
	}
	free(node);
	ret = 0;
err:
	return ret;
}


int tq_remove_thread(struct tq * tq, thread_id thread_to_remove)
{
	int ret = -1;
	if ((!tq) || (!tq->head))
		goto err;

	struct node * node;
	struct node * prev; /* The unit previouse to 'node' */

	node = tq->head;
	prev = NULL;
	while ((NULL != node) && (node->payload != thread_to_remove)) {
		prev = node;
		node = node->next;
	}
	if (NULL == node) {
		goto err;
	}
	assert (node->payload == thread_to_remove);
	/* 'node' points to the wanted node. */
	if (NULL != prev) {
		assert (node != tq->head);
		assert (prev->next == node);

		prev->next = node->next;
	} else {
		assert (node == tq->head);
	}

	if (node == tq->head) {
		tq->head = node->next;
	}
	if (node == tq->tail) {
		tq->tail = prev;
	}

	free(node);
	ret = 0;
err:
	return ret;
}


bool tq_is_empty(struct tq * tq) {
	return (tq->head == NULL);
}
